package uk.ac.aber.Blockmation;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

import javax.swing.SwingUtilities;


/**
 * The main drawing area. Extends Display class.
 * Draws preview color blocks to ensure user see where he can draw.
 * 
 * @author Aron
 */
public class DrawingPad extends Display 
		implements MouseMotionListener, MouseListener
{
    final static Color COLOR = Color.black;
    private Point mousePos = new Point(0, 0);
    private boolean gridOn = false;
    
	/** the drawing color */
    private Color color;
    
	/** color as selected with one of mouse buttons clicks */
    private Color mouseButtonsColor;
    private Color gridColor;

    /* constants representing drawing tools */
    public static final int POINT = 0;
    public static final int RECT = 1;
    public static final int OVAL = 2;
    
    private int currentTool = 0;
    private Point mouseReleasedPt;
    private Point mousePressedPt;
    
    private MainFrame mainFrame;
    
    /**
     * Creates new object of DrawingPad with default color pen,
     * and gridColor to be same.
     * It's related to MainFrame object.
     * @param mainFrame
     */
    public DrawingPad(MainFrame mainFrame)
    {
    	color = COLOR;
    	gridColor = color;
        this.addMouseMotionListener(this);
        this.addMouseListener(this);
        
        this.mainFrame = mainFrame;
    }

    /**
     * Main drawing method of the DrawingPad component.
     * Simply displays image contained by superclass.
     * <br />
     * On top of this, but on actual canvas of this panel, draws preview color 
     * blocks
     * at mouse location in such a way that while user remains within one pixel
     * in the original image the preview block stays in place in spite of moving
     * mouse within the block. 
     * <br />
     * That's giving the illusion of drawing with squares.
     * Look up Display class for explanation of pixel to block scaling.
     * 
     * <br />
     * Here the grid is drawn on the panel canvas also. 
     */
    public void paint(Graphics g)
    {
        super.paint(g);

        Graphics2D g2 = (Graphics2D) g;
        
        BufferedImage image = super.getDisplayedImage();
        Dimension block = super.getBlockSize();
        
        if (image != null)
        {
            g2.drawImage(image, null, 0, 0);
        }

        // check whether within image or outside on panel first
        Rectangle imgArea = new Rectangle(image.getWidth(), image.getHeight());
        
        int fullX = (int) Math.floor(mousePos.getX() / block.width);
        int fullY = (int) Math.floor(mousePos.getY() / block.height);
        int upX = fullX * block.width;
        int upY = fullY * block.height;
        
        // draw only if hovering on the image, not panel, which can be bigger
        // than the image itself
        if (imgArea.contains(upX, upY))
        {
        	g2.setColor(color);
        	g2.fillRect(upX, upY, block.width, block.height);
        }
        
        // adds extra border around the image
        g2.drawRect(0, 0, image.getWidth(), image.getHeight());
        
        // draws the grid if set
        if (gridOn)
        {
	        for (int y=0; y < image.getHeight(); y+=block.height)
	        	for (int x=0; x < image.getWidth(); x+=block.width)
	        	{
	        		g2.setColor(gridColor); // sets the color of the grid
	        		g2.drawRect( x, y, block.width, block.height);
	        	}
        }
        
        
        g2.dispose();
        
    }

    /**
     * When mouse is dragged, draw squares too.
     */
    public void mouseDragged(MouseEvent e)
    {
    	drawWithAppropriateTool(e);
    }

    /**
     * When mouse's moving record the position on 
     * repaint so the user can see where he is on the screen
     * (preview block).
     */
    public void mouseMoved(MouseEvent e)
    {
        mousePos = e.getPoint();
        repaint(); // repainting squared edge
    }

    public void mouseClicked(MouseEvent e)
    {
    }

    public void mouseEntered(MouseEvent e)
    {
    }
   
    /**
     * When mouse's moved away from the pad, don't display the preview block.
     */
    public void mouseExited(MouseEvent e)
    {
    	mousePos = new Point(-1, -1);
    	repaint();
    }

    /**
     * Stores the position of mouse pressed for drawing with shape.
     */
    public void mousePressed(MouseEvent e)
    {
    	mousePressedPt = e.getPoint();
    	drawWithAppropriateTool(e);
    	
    }

    /**
     * Stores the position of mouse releasing and actually
     * performs drawing a shape if selected by method setTool().
     */
    public void mouseReleased(MouseEvent e)
    {
    	mouseReleasedPt = e.getPoint();
    	
       	switch (currentTool)
    	{
    		case RECT: drawShape(); break;
    		case OVAL: drawShape(); break;
    	}
    	
 
    	
    }
    
    /**
     * Draws one of the two shapes: OVAL or RECTANGLE as set 
     * by method setTool(). Uses simple way of drawing straight to the 
     * actual image with use of superclass getImage() and setImage() methods.
     * Doesn't show preview of performing draw. Draws from the point mouse was 
     * pressed to the point mouse is released.
     * 
     * <br />
     *  Updates the MovieModel with this change.
     * <em> Related to MainFrame class which is not very OO concept - 
     * justified by this being a learning curve. However, by removing the few 
     * lines with mainFrame updates, remains very OO, so not too bad :). <em>
     */
    public void drawShape()
    {
    	
    	BufferedImage smallBim = super.getImage(); 
    	Graphics2D g2 = smallBim.createGraphics();
    	g2.setColor(mouseButtonsColor);
    	int upX = (int)getActualIntPoint(mousePressedPt).getX();
    	int upY = (int)getActualIntPoint(mousePressedPt).getY();
    	int downX = (int)getActualIntPoint(mouseReleasedPt).getX();
    	int downY = (int)getActualIntPoint(mouseReleasedPt).getY();
    	System.out.format("upX %d, upY %d, downX %d, downY %d\n", upX, upY, downX, downY);
    	int width = downX - upX;
    	int height = downY - upY;
    	
       	switch (currentTool)
    	{
    		case RECT: g2.drawRect(upX, upY, width, height); break;
    		case OVAL: g2.drawOval(upX, upY, width, height); break;
    		default: System.err.println("There is no such tool!"); return;
    	}
       	
    	g2.dispose();
    	super.setImage(smallBim); 
    	
        // updating movieModel with the current changes in pad
        mainFrame.getMovieModel().setFrame(mainFrame.getCurrentFrameIndex(), super.getImage());
        mainFrame.getCenterBottomPanel().updateCurrentThumb();
    }

    /**
     * Registers whether right or left mouse button's been clicked
     * and assigns specific color to draw with.
     * If selectedTool is square then here's when it actually draws the sqaure.
     * Used inside MouseMotionListener
     * @param e
     */
    private void drawWithAppropriateTool(MouseEvent e)
    {
        if (SwingUtilities.isLeftMouseButton(e))
        {
        	mouseButtonsColor = color;
        }
        else
        {
        	mouseButtonsColor = super.getBackgroundColor();
        }
        
        switch (currentTool)
        {
        case POINT:	drawSquare(e.getPoint(), mouseButtonsColor); break;
        }
        
    }
    
    /**
     * @return drawing color
     */
    public Color getColor()
    {
        return color;
    }
    
    /**
     * Sets drawing color
     * @param color - new color
     */
    public void setColor(Color color)
    {
        this.color = color;
    }
    
    /**
     * This method actually draws the square on the displayed image.
     * 
     * <br />
     *  Updates the MovieModel with and current thumbnail with this change.
     * <em> Related to MainFrame class which is not very OO concept - 
     * justified by this being a learning curve. However, by removing the few 
     * lines with mainFrame updates, remains very OO, so not too bad :). <em>
     * 
     * @param position where to draw the square from
     * @param squareColor
     */
    public void drawSquare(Point position, Color squareColor)
    {
        mousePos = position;
        
        BufferedImage image = super.getDisplayedImage();
        Dimension block = super.getBlockSize();

        // Note: the graphics context is the image not the panel context
        Graphics2D g2 = image.createGraphics();
        g2.setColor(squareColor);

        /* 
         * ensures the square is drawn at a position in which
         * it precisely corresponds to the pixel in original image
         */ 
        int fullX = (int) Math.floor(position.getX() / block.width);
        int fullY = (int) Math.floor(position.getY() / block.height);

        g2.fillRect(fullX * block.width, fullY * block.height, block.width,
            block.height);
        g2.dispose();

        repaint();
        
        // updating movieModel with the current changes in pad
        mainFrame.getMovieModel().setFrame(mainFrame.getCurrentFrameIndex(), super.getImage());
        mainFrame.getCenterBottomPanel().updateCurrentThumb();
    }
    
    /**
     * Shows the grid as long as the displayed image is still visible 
     * (when block size is bigger than 1/1).
     * @param showGrid - true if turn on, false otherwise
     * @return false - if couldn't show the grid
     */
    public boolean showGrid(boolean showGrid)
    {
    	// only allow if block size is not too small  
        Dimension block = super.getBlockSize();
    	
    	if (block.width > 1 && block.width > 1)
    	{
        	if (showGrid) gridOn = true; else gridOn = false;
        	
        	repaint();
        	return true;
    	}
    		return false;// disallow if block size too small
    }
    
    /**
     * Translates mouse poistion (point) from scalled displayed image
     * into the actual point on the original image.
     * @param zoomedPt
     * @return actual point
     */
    private Point getActualIntPoint(Point zoomedPt)
    {
    	Dimension block = super.getBlockSize();
    	Point actualPt = new Point((int)zoomedPt.getX()/block.width, (int)zoomedPt.getY()/block.height);
    	
    	return actualPt;
    }

    /**
     * 
     * @return the selected tool
     */
    public int getCurrentTool() {
            return currentTool;
    }

    /**
     * Sets the drawing methods to draw with this too. 
     * @param newTool
     */
    public void setTool(int newTool) {

            // set only if within available tools
            if (newTool >= 0 && newTool < 3)
            this.currentTool = newTool;
    }

    public Color getGridColor() {
            return gridColor;
    }

    public void setGridColor(Color gridColor) {
            this.gridColor = gridColor;
    }

    public Point getMousePos() {
            return mousePos;
    }

    public Point getMousePressedPt() {
            return mousePressedPt;
    }

    public Point getMouseReleasedPt() {
            return mouseReleasedPt;
    }

    public boolean isGridOn() {
            return gridOn;
    }
	
	
}

